package transport

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"sync"
	"sync/atomic"
	"time"
)


type UnixClient struct {
	conn net.Conn
	addr string
	opt DialOptions
}

func (m *UnixClient) Recv(msg *Message) error{
	if msg == nil{
		msg = new(Message)
	}
	data,err := ioutil.ReadAll(m.conn)
	if err != nil{
		return err
	}
	if err =  json.Unmarshal(data,msg);err != nil{
		return err
	}
	return nil
}

func(m *UnixClient) Send(msg *Message) error{
	if msg == nil{
		return fmt.Errorf("param bad")
	}
	data,err := json.Marshal(msg)
	if err != nil{
		return err
	}
	writer := bufio.NewWriter(m.conn)
	_,err = writer.Write(data)
	if err != nil{
		return err
	}
	if err := writer.Flush();err != nil{
		return err
	}
	return nil
}

func (m *UnixClient) Close() error{
	return m.conn.Close()
}


type UnixLister struct {
	lister net.Listener
	addr string
	opts ListenOptions
	stop int32
	wait sync.WaitGroup
}

func (m *UnixLister) Addr() string{
	return fmt.Sprintln("unix socket addr:",m.addr)
}

func(m *UnixLister) Close() error{
	m.stop =2
	m.wait.Wait()
	return m.lister.Close()
}

func (m *UnixLister) Accept(f func(socket Socket)) error{
	defer m.wait.Add(-1)
	m.wait.Add(1)
	for{
		if atomic.CompareAndSwapInt32(&m.stop,2,1){
			return nil
		}
		conn,err :=	m.lister.Accept()
		if err != nil{
			if ne,ok := err.(net.Error);ok && ne.Temporary(){
				time.Sleep(time.Second *10)
			}else{
				return err
			}
		}
		f(&UnixClient{conn:conn})
	}
	return nil
}


type UnixTransport struct {
	opt Options
}

func (u *UnixTransport) Init(opts ...Option) error{
	for _,o := range opts{
		o(&u.opt)
	}
	return nil
}

func (u *UnixTransport) Dial(addr string ,opts ...DialOption)(Client,error){
	opt := DialOptions{}
	for _,o := range opts{
		o(&opt)
	}

	conn,err := net.DialTimeout("unix",addr,opt.Timeout)
	if err != nil{
		return nil,err
	}
	return &UnixClient{conn:conn,addr:addr},nil
}

func (u *UnixTransport) Listen(addr string,opts ...ListenOption)(Listener,error){
	os.Remove(addr)
	unixAddr,err := net.ResolveUnixAddr("unix",addr)
	if err != nil{
		return nil,err
	}
	l,err := net.ListenUnix("unix",unixAddr)
	if err != nil{
		return nil,err
	}
	return &UnixLister{lister:l,addr:addr,stop:1},nil
}

func (u *UnixTransport) String()string{
	return fmt.Sprintln("unix transport")
}

func NewDefaultTransport(opts ...Option)Transport{
	tran :=  new(UnixTransport)
	tran.Init(opts...)
	return tran
}